package club.xyes.zkh.ecommerce.service.impl;

import club.xyes.zkh.ecommerce.commons.entity.BaseEntity;
import club.xyes.zkh.ecommerce.commons.exception.InternalServerErrorException;
import club.xyes.zkh.ecommerce.commons.exception.ResourceNotFoundException;
import club.xyes.zkh.ecommerce.dao.BaseMapper;
import club.xyes.zkh.ecommerce.service.BaseService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Create by 郭文梁 2019/4/15 0015 15:18
 * BaseServiceImpl
 * 业务服务组建基类实现
 *
 * @author 郭文梁
 * @data 2019/4/15 0015
 */
@Slf4j
@SuppressWarnings("unchecked")
public abstract class BaseServiceImpl<T extends BaseEntity> implements BaseService<T> {
    private final Class<? extends BaseEntity> modelClazz;
    private final BaseMapper<T> mapper;

    public BaseServiceImpl(BaseMapper<T> mapper) {
        this.mapper = mapper;
    }

    {
        Type superclass = getClass().getGenericSuperclass();
        while (!(superclass instanceof ParameterizedType)) {
            if (superclass instanceof Class) {
                Class<?> clazz = (Class<?>) superclass;
                superclass = clazz.getGenericSuperclass();
            } else {
                throw new IllegalArgumentException("The superclass of service must be a Adapter or BaseServiceImpl");
            }
        }
        ParameterizedType type = (ParameterizedType) superclass;
        modelClazz = (Class<? extends BaseEntity>) type.getActualTypeArguments()[0];
    }

    @Override
    public List<T> all() {
        return mapper.selectAll();
    }

    @Override
    public PageInfo<T> list(int page, int rows) {
        return PageHelper.startPage(page, rows).doSelectPageInfo(mapper::selectAll);
    }

    @Override
    public T get(Integer id) {
        return mapper.selectByPrimaryKey(id);
    }

    @Override
    public T getRequired(Integer id) throws ResourceNotFoundException {
        T o = get(id);
        if (o == null) {
            throw new ResourceNotFoundException(modelClazz, id);
        }
        return o;
    }

    @Override
    public T updateById(T o) {
        o.touch();
        int col = mapper.updateByPrimaryKey(o);
        if (col != 1) {
            throw new InternalServerErrorException("Unable to update entity:" + o);
        }
        return o;
    }

    @Override
    public T save(T o) {
        o.init();
        int col = mapper.insert(o);
        if (col != 1) {
            throw new InternalServerErrorException("Could not save entity:" + o);
        }
        return o;
    }

    @Override
    public List<T> save(List<T> os) {
        if (os == null || os.size() <= 0) {
            return null;
        }
        os = os.stream().peek(BaseEntity::init).collect(Collectors.toList());
        int col = mapper.insertList(os);
        if (col == os.size()) {
            return os;
        } else {
            throw new InternalServerErrorException("Save error: length of expect to save is " +
                    os.size() + ", but Actual storage length is " + col + ", data: " + os);
        }
    }

    @Override
    public List<T> findByQuery(T query) {
        return mapper.select(query);
    }

    @Override
    public T findOneByQuery(T query) {
        return mapper.selectOne(query);
    }

    @Override
    public boolean deleteById(Integer id) {
        int col = mapper.deleteByPrimaryKey(id);
        log.debug("Delete {} where id={}", modelClazz, id);
        if (col > 1) {
            throw new InternalServerErrorException("Delete Error: result is " + col);
        }
        return col == 1;
    }

    @Override
    public int count() {
        return count(null);
    }

    @Override
    public int count(T query) {
        return mapper.selectCount(query);
    }

    @Override
    public boolean exists(T query) {
        return count(query) > 0;
    }

    @Override
    public int delete(T query) {
        return mapper.delete(query);
    }
}
